/*
Brick And Blast, a crossplatform breakout game.
 Copyright (C) 2008-2010 Julien CLEMENT (andematunum@gmail.com)
 
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

#include "Ball.h"
#include "Bat.h"
#include "BatPart.h"
#include "Bumper.h"
#include "Edge.h"
#include "Collider.h"

/**
* file   Ball.cpp
* date   Ven 25 déc 2009 20:47:00 CET
* brief  TODO
*/

Ball::
Ball(SDL_Point& initPosition,
     SDL_TextureID initTexture,
     double angle, double velocity)
  : GameObject(GAME_OBJECT_BALL,GAME_OBJECT_CIRCLE,initPosition,initTexture),
    angle_(angle),
    velocity_(velocity) {
 
    update_angle();
    set_position(initPosition);
}

void
Ball::
set_position (SDL_Point& xy) {
  GameObject::set_position(xy);
  center_x_ = static_cast<double>(xy.x);
  center_y_ = static_cast<double>(xy.y);
}

void
Ball::
move () {
  center_x_ += (dx_*accel_decel_);
  center_y_ += (dy_*accel_decel_);
  if (accel_decel_ > 1.001) {
    accel_decel_/=1.05;
  }
  else {
    accel_decel_ = 1;
  }
}

void
Ball::
update_angle () {
  dx_ = velocity_ * cos(angle_);
  dy_ = velocity_ * sin(angle_);
}

void
Ball::
calculate_next_pixel_position () {
  pixel_position_next_.x = static_cast<int>(round(center_x_ + dx_));
  pixel_position_next_.y = static_cast<int>(round(center_y_ + dy_));
  pixel_position_changed_ = 
   ((pixel_position_next_.x != pixel_position_.x) || (pixel_position_next_.y != pixel_position_.y));
  calculate_next_pixel_rect();
}

bool
Ball::
handle_game_area_collisions () {
  int radius = size_.r;
  bool collides = false;
  if ((pixel_position_next_.x - radius < 16) || (pixel_position_next_.x + radius >= 1008)) {
    collide_horizontal();
    collides = true;
  }
  if ((pixel_position_next_.y - radius < 16) || (pixel_position_next_.y + radius >= 672)) {
    collide_vertical();
    collides = true;
  }
  return collides;
}
  
void
Ball::
handle_collision (GameObject* obj, const ColliderData& data) {
  switch(obj->get_type()) {
    case GAME_OBJECT_BALL: {
      Ball* ball = static_cast<Ball*>(obj);
      handle_collision(ball, data);
   }
   break;
   case GAME_OBJECT_BAT_BORDER_LEFT:
   case GAME_OBJECT_BAT_BORDER_RIGHT:
   case GAME_OBJECT_BAT_CHUNK: {
    BatPart* part = static_cast<BatPart*>(obj);
    handle_collision(part, data);
   }
   break;
   case GAME_OBJECT_BUMPER: {
    Bumper* bumper = static_cast<Bumper*>(obj);
    handle_collision(bumper, data);
   }
   break;
   case GAME_OBJECT_EDGE: {
    Edge* edge = static_cast<Edge*>(obj);
    handle_collision(edge, data);
   }
   break;
   default: {
     cout << "*** WARNING: Collision with object of type: " << obj->get_type() << " not handled." << endl;
   }
   break;
  }
}

void
Ball::
handle_collision (Ball* other, const ColliderData& data) {
  int vx = data.geom.center_vector.x;
  int vy = data.geom.center_vector.y;
  
  double new_angle;
  
  if (vx > 0) {
    new_angle = atan((double)vy / (double)vx);
  }
  else if (vx < 0) {
    new_angle = M_PI - atan((double)vy / -(double)vx);
  }
  else {
    new_angle = (vy >= 0) ? (+M_PI) : (-M_PI); 
  }

  if (this == data.from_object) {
    if (new_angle < M_PI) {
      set_angle(new_angle + M_PI);  
    }
    else {
      set_angle(new_angle - M_PI);
    }
  }
  else {
    set_angle(new_angle);
  }
}

void
Ball::
handle_collision (BatPart* bat, const ColliderData& data) {
  const SDL_Rect* bat_rect = bat->get_bat()->get_rect();
  // Translate to the real origin of our problem:
  // Do as if the left border of the bat minus the radius of
  // the ball is the origin, for which the angle is +PI
  int orig = bat_rect->x - size_.r;
  int width = bat_rect->w + 2*size_.r;
  double angle = (double)((center_x_ - orig - width) * M_PI) / (double)width;
  cout << "Bat collision, angle = " << angle * 360. / (2.*M_PI) << endl;
  set_angle(angle);
}

/**
  * Bumper collision is nearly the same as with a ball except
  * that the bumper never moves (no handler in Bumper class)
  * and the ball gets accel/deccel
  */
void
Ball::
handle_collision (Bumper* bumper, const ColliderData& data) {
  int vx = data.geom.center_vector.x;
  int vy = data.geom.center_vector.y;
  
  double new_angle;
  
  if (vx > 0) {
    new_angle = atan((double)vy / (double)vx);
  }
  else if (vx < 0) {
    new_angle = M_PI - atan((double)vy / -(double)vx);
  }
  else {
    new_angle = (vy >= 0) ? (+M_PI) : (-M_PI); 
  }

  if (this == data.from_object) {
    if (new_angle < M_PI) {
      set_angle(new_angle + M_PI);  
    }
    else {
      set_angle(new_angle - M_PI);
    }
  }
  else {
    set_angle(new_angle);
  }

  set_accel_decel(3.5);
}

void
Ball::
handle_collision (Edge* edge, const ColliderData& data) {
  switch(data.geom.rect_circle_config) {
    case 1: {
      collide_vertical();
      return;
    }
    case 2: {
      collide_horizontal();
      collide_vertical();
      return;
    }
    case 3: {
      collide_horizontal();
      return;
    }
    default: {
/*      cerr << "*** ERROR: Unexpected collision config with edge: " <<
          data.geom.rect_circle_config << endl;*/
    }
  }
}

